#include <unistd.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <sys/poll.h>
#include <getopt.h>
#include <stdio.h>

#include "dataDevice.h"

using namespace std;

static const int timeout = 5; //seconds
char buff[65535];

int main(int argc, char **argv)
{
  int c;

  int errFlag = 0,
      daemonFlag = 0,
      helpFlag = 0;

  string dataDeviceSerial,
         speed,
         heartbeatServer,
         deviceName = "serial";

  int heartbeatPort = -1,
      cmdPort = -1,
      nodeID = -1;

  struct option opts[] = 
  {
    {"serial", required_argument, 0, 'a'},
    {"speed", required_argument, 0, 'b'},
    {"server", required_argument, 0, 's'},
    {"port", required_argument, 0, 'p'},
    {"listenPort", required_argument, 0, 'l'},
    {"id", required_argument, 0, 'i'},
    {"daemon", no_argument, 0, 'd'},
    {"help", no_argument, 0, 'h'},
    {0, 0, 0, 0}
  };

  while((c = getopt_long(argc, argv, "dha:b:s:p:l:i:", opts, NULL)) != EOF)
  {
    switch(c)
    {
      case 'a':
        dataDeviceSerial = optarg;
        break;
      case 'b':
        speed = optarg;
        break;
      case 's':
        heartbeatServer = optarg;
        break;
      case 'p':
        heartbeatPort = atoi(optarg);
        break;
      case 'l':
        cmdPort = atoi(optarg);
        break;
      case 'i':
        nodeID = atoi(optarg); 
        break;
      case 'd':
        daemonFlag = 1;
        break;
      case 'h':
        helpFlag = 1;
        break;
      case '?':
        errFlag++;
        break;
      default:
        break;
    }
  }

  if(dataDeviceSerial.empty() || speed.empty() || heartbeatServer.empty() || -1 == heartbeatPort || -1 == cmdPort || -1 == nodeID)
    errFlag++;

  if(errFlag || helpFlag)
  {
    cerr<<"Usage: heartbeartClient [OPTION]"<<endl<<endl
        <<"  --serial          Serial to control camera"<<endl
        <<"  --speed           Speed of serial"<<endl
        <<"  -s, --server      Address of heartbeat server"<<endl
        <<"  -p, --port        Heartbeat server port"<<endl
        <<"  -l, --listenPort  Listen on this UDP port for receive data from heartbeat server"<<endl
        <<"  -i, --id          ID number of this node"<<endl
        <<"  -d, --daemon      Set program to daemon mode"<<endl
        <<"  -h, --help        Print help message"<<endl<<endl;
    exit(0);
  }

  if(daemonFlag)
    daemon(0, 0);

  int res;

  //----------------------------------------------------------------------------------
  //Get conf
  /*get configure info*/

  /* get device configure */
  cout<<"Serial device"<<endl;

  /*Listen port*/
  cout<<"Listen port: "<<cmdPort<<endl;

  /*get heartbeat server address*/
  cout<<"Heartbeat erver address: "<<heartbeatServer<<" , Port: "<<heartbeatPort<<endl;

  /* create device */
  dataDevice *device;
  if(deviceName == "serial")
  {
    device = new serialDataDevice(dataDeviceSerial.c_str(), speed);
  }

  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

  /* open device */
  device->open();

  /* create socket for receive cmd */
  int fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(fd == -1)
  {
    cerr<<"Create socket for receive cmd error: "<<strerror(errno)<<endl;
    exit(1);
  }
  cout<<"socket fd = "<<fd<<endl;

  /* build local address */
  struct sockaddr_in addr;
  bzero(&addr, sizeof(struct sockaddr_in));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(cmdPort);
  addr.sin_addr.s_addr = htonl(INADDR_ANY);

  /*build heartbeat server address*/
  struct sockaddr_in haddr;
  bzero(&haddr, sizeof(struct sockaddr_in));
  haddr.sin_family = AF_INET;
  haddr.sin_port = htons(heartbeatPort);
  inet_pton(AF_INET, heartbeatServer.c_str(), &haddr.sin_addr);

  int reuse = 1;
  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
  if(bind(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr)) == -1)
  {
    cerr<<__func__<<": Bind error: "<<strerror(errno)<<endl;
    close(fd);
    exit(1);
  }
  cout<<"bind address successful!"<<endl;

  //accept or send message

  //---------------Heartbeat subprocess------------------
  /*heartbeat*/
  if(0 == fork()) //child process
  {
    cout<<"Heartbeat"<<endl;
    char sendB[10];
	  bzero(sendB, sizeof(sendB));
	  sendB[0] = '\x02';
    res = sprintf(sendB + 1, "%03d", nodeID % 1000);
	  cout<<res<<endl;
    while(true)
    {
      if(1 == getppid())
      {
        cout<<"Parent process exited..."<<endl;
        exit(0);
      }
      sendto(fd, sendB, res + 1, 0, (struct sockaddr *)&haddr, sizeof(struct sockaddr));
      cout<<"~~Heartbeat: Send OK."<<endl;
      sleep(10);
    }
    close(fd);
  }
  //+++++++++++++++++End of Heartbeat Subprocess+++++++++

  //accept or send message

  struct sockaddr_in cliaddr;
  socklen_t clilen = sizeof(cliaddr);

  struct pollfd fds[1];
  //UDP file descriptor
  fds[0].fd = fd;
  fds[0].events = POLLIN;

  while(true)
  {
    res = poll(fds, 1, -1);
    if(res < 0)
    {
      cout<<"Poll error, exit...."<<endl;
      exit(1);
    }
    else if(res == 0)
    {
      cout<<"Poll timeout"<<endl;
      continue;
    }

    /*Data from socket*/
    if(fds[0].revents & POLLIN)
    {
      bzero(buff, sizeof(buff));
      res = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&cliaddr, &clilen);
      cout<<"Node: Read "<<res<<" bytes from heartbeat server."<<endl;
      if(res == 0)
      {
        cout<<"Disconnected from gateway"<<endl;
        break;
      }
      else if(res > 0)
      {
	      if(buff[0] == '\x00') //command
		    {
		      for(int i=0;i<res;i++)
		        printf("%02hhx ", buff[i]);
		      cout<<endl;
		      cout<<"cmd"<<endl;
	        if(buff + 1 == memmem(buff + 1, 2, "on", 2))
		      {
            /* Open camera device and create control connect */
		        cout<<"Open"<<endl;
			      system("sh control/on.sh");
		      }
		      else if(buff + 1 == memmem(buff + 1, 3, "off", 3))
		      {
            /* Close camera device and close control connect */
		        cout<<"Close"<<endl;
			      system("sh control/off.sh");
		      }
		      else if(buff + 1 == memmem(buff + 1, 6, "reboot", 6))
					{
					  cout<<"Reboot"<<endl;
            system("reboot");
					}
		    }
        else if(buff[0] == '\x01')
		    {
		      cout<<"data"<<endl;
          cout<<"Write to device"<<endl;
          for(int i=0;i<res-1;i++)
            printf("%02hhx ", buff[i+1]);
          cout<<endl;
		      device->write(buff + 1, res - 1);
		    }
      }
      else
      {
        if(errno == EINTR)
          continue;
        cout<<"Node: recv from gateway error"<<endl;
        break;
      }
    }//end if
  }//end while
  
  close(fd); //close listen fd
  return 0;
}//end main
